/* 
 bBackup folder copy
 Version 4
 By Ben a.k.a DreamVB
*/

#include <iostream>
#include <Windows.h>
#include <string>
#include <algorithm>
#include <conio.h>

using namespace std;
using std::cout;
using std::endl;

int Copyed = 0;
int Skipped = 0;
//
bool OverReadOnly = false;
bool bCopyFiles = true;
bool NoMsg = false;
bool ShowFullName = false;
bool ShowStatusInfo = false;
bool ShowStartMsg = false;
bool SetAchiveOnlyAttr = false;

string sStatusA = "";
string sStatusB = "";

string LCase(string src)
{
	string Temp = src;
	std::transform(Temp.begin(), Temp.end(), Temp.begin(), ::tolower);
	return Temp;
}

string FixPath(string src){
	if (src[src.length() - 1] != '\\'){
		return src + "\\";
	}
	return src;
}

bool FileExists(string filename){
	//Check if a file was found.
	if (GetFileAttributesA(filename.c_str()) == INVALID_FILE_ATTRIBUTES){
		return false;
	}
	return true;
}

bool DirExists(string filename){
	if ((GetFileAttributesA(filename.c_str()) != FILE_ATTRIBUTE_DIRECTORY)){
		return false;
	}
	return true;
}

void MyCreateDir(string PathName){
	int i = 0;
	int j = 0;
	string sPart = "";
	string sDir = "";

	//Append end backslash.
	if (!PathName[PathName.length() - 1] != '\\'){
		PathName += "\\";
	}
	//
	while (i < PathName.length()){
		if (PathName[i] == '\\'){
			//The path to create
			sDir += sPart + "\\";
			//Check for folder.
			if ((GetFileAttributesA(sDir.c_str()) & FILE_ATTRIBUTE_DIRECTORY)){
				//Create folder
				CreateDirectoryA(sDir.c_str(), NULL);
			}
			//Clear parts.
			sPart.clear();
		}
		else{
			//Split the string by backslash
			sPart += PathName[i];
		}
		//INC counter
		i++;
	}
	sPart.clear();
	sDir.clear();
}

void DirTree(string Path, string lzDestPath, string FileType, bool AllDirs){
	HANDLE f = 0;
	WIN32_FIND_DATAA wfd;
	string lzPath = "";
	string lzCopyTo = "";
	string NewPath = "";
	string lzSrcFile = "";
	string lzDestFile = "";
	string Temp = "";
	string fExt = "";
	int pos = string::npos;

	//Append backslash to path.
	lzPath = FixPath(Path);

	//Find first file.
	f = FindFirstFileA((lzPath + "*.*").c_str(), &wfd);

	//Check for inaild path.
	if (f == INVALID_HANDLE_VALUE){
		cout << "Folder or files not found." << endl << lzPath.c_str() << endl;
		FindClose(f);
		exit(1);
	}

	//Gets the list of files
	do{
		if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
			//Build file path

			if (wfd.cFileName[0] != 46){
				NewPath = FixPath(lzPath + wfd.cFileName);

				//Scan all sub folders.
				if (AllDirs){
					if (DirExists(NewPath)){
						DirTree(NewPath, lzDestPath, FileType, AllDirs);
					}
				}
			}

		}
		else{
			if (FileType.length() != 0){
				fExt = LCase(wfd.cFileName);
				pos = fExt.find_last_of('.');
				if (pos != string::npos){
					fExt.erase(0, pos);
					if (fExt == FileType){
						//Set src filename
						lzSrcFile = lzPath + wfd.cFileName;
					}
				}
			}
			else{
				//Set src filename
				lzSrcFile = lzPath + wfd.cFileName;
			}
			//Test if we have a filename.
			if (lzSrcFile.length() != 0){
				//Set dest filename
				Temp = lzSrcFile;
				pos = Temp.find_first_of('\\');
				if (pos != string::npos){
					Temp.erase(0, pos + 1);
				}

				//Set dest filename
				lzDestFile = lzDestPath + Temp;
				//Extract the dest full path.
				pos = lzDestFile.find_last_of('\\');
				if (pos != string::npos){
					lzCopyTo = lzDestFile.substr(0, pos);
				}

				//Create destination folders
				if (!DirExists(lzCopyTo)){
					MyCreateDir(lzCopyTo);
				}

				//Check if copying files
				if (bCopyFiles){
					//Check if writeing over old file.
					if (OverReadOnly){
						if (FileExists(lzDestFile)){
							//Remove all file attrib and set to normal
							SetFileAttributesA(lzDestFile.c_str(), FILE_ATTRIBUTE_NORMAL);
						}
					}

					//Copy the src file over to the folder.
					if (!CopyFileA(lzSrcFile.c_str(), lzDestFile.c_str(), false)) {
						if (!NoMsg){
							if (ShowFullName){
								cout << lzDestFile.c_str() << sStatusB << endl;
							}
							else{
								cout << wfd.cFileName << sStatusB << endl;
							}
							Skipped++;
						}
					}
					else{
						if (!NoMsg){
							if (ShowFullName){
								cout << lzDestFile.c_str() << sStatusA << endl;
							}
							else{
								cout << wfd.cFileName << sStatusA << endl;
							}
							Copyed++;
						}
						//Set file attrib to achive
						if (SetAchiveOnlyAttr){
							SetFileAttributesA(lzDestFile.c_str(), FILE_ATTRIBUTE_ARCHIVE);
						}
					}
					lzSrcFile.clear();
					Temp.clear();
				}
			}
		}
	} while (FindNextFileA(f, &wfd) != 0);

	//Close file
	FindClose(f);
}

int main(int argc, char *argv[]){
	string Temp = "";
	string DeskFolder = "";
	string fExt = "";
	string Flags = "";
	bool ScanAllDirs = false;
	int pos = string::npos;
	int i = 3;

	//Check for help switch
	if (argc == 2){
		Flags = argv[1];
		//If help switch display online help
		if (Flags == "/?"){
			cout << "Backup Folders version 3.1" << endl << endl;
			cout << argv[0] << " <source> <destination> [/A] [/C] [/D] [/F] [/I] [/Q] [/R] [/S] [/W]" << endl << endl;
			cout << " source         Specifies the folder with the file(s) to copy." << endl;
			cout << " destination    Specifies were the file(s) should be copied to." << endl;
			cout << " /A             Sets the destination files(a) to achive attribute." << endl;
			cout << " /D             When set only folders structures are created." << endl;
			cout << " /F             When set displays the full filename being copied." << endl;
			cout << " /I             When set file status is shown." << endl;
			cout << " /Q             When set no messages are displayed while copying." << endl;
			cout << " /R             Writes over files that are read-only." << endl;
			cout << " /S             When set all sub folders are included in backup." << endl;
			cout << " /W             Displays a message and waits for a key press." << endl;
			cout << endl;
			exit(1);
		}
		exit(1);
	}
	//
	//Check argv
	if (argc < 3){
		cout << "Usage: " << argv[0] << " <source> <destination> [options]" << endl << endl;
		cout << "For more information see " << argv[0] << " /?" << endl;
		exit(1);
	}

	//Get other args
	while (i < argc){
		Flags = LCase(argv[i]);
		//All files attributes are set to achive when set
		if (Flags == "/a"){
			SetAchiveOnlyAttr = true;
		}
		//If set all sub folders are scanned.
		if (Flags == "/s"){
			ScanAllDirs = true;
		}
		//Ovwerwrite read only files.
		if (Flags == "/r"){
			OverReadOnly = true;
		}
		//if set no on-screen messages are shown
		if (Flags == "/q"){
			NoMsg = true;
		}
		//When set only folder structure is created.
		if (Flags == "/d"){
			bCopyFiles = false;
		}
		//Displays full filenames
		if (Flags == "/f"){
			ShowFullName = true;
		}
		//Display file status if set
		if (Flags == "/i"){
			ShowStatusInfo = true;
		}
		//Display a message before copying if set
		if (Flags == "/w"){
			ShowStartMsg = true;
		}
		//INC counter
		i++;
	}

	cout << "The copy process is about to begin press any key to continue.";
	
	if (ShowStartMsg){
		while (1){
			if (kbhit()){
				break;
			}
		}
	}

	if (ShowStatusInfo){
		sStatusA = "....OK";
		sStatusB = "....Skipped";
	}

	//Source folder
	Temp = argv[1];
	//Destination folder
	DeskFolder = FixPath(argv[2]);

	pos = Temp.find('*');

	if (pos != string::npos){
		fExt = LCase(Temp.substr(pos + 1));
		if (fExt == ".*"){
			fExt = "";
		}
		//Fix the path
		Temp.erase(pos);
	}

	//Call DirTree procedure
	DirTree(Temp, DeskFolder, fExt, ScanAllDirs);

	if (!NoMsg){
		//Display results
		cout << endl;
		cout << "Copyed  : " << Copyed << endl;
		cout << "Skipped : " << Skipped << endl;
	}

	return EXIT_SUCCESS;
}

